﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Ivory;
using DefCore.Cameras;
using DefCore.Mapping;
using DefCore.Core;
using Krypton.Lights;
using Krypton;
using System.Diagnostics;
using Microsoft.Xna.Framework.Input;
using DefCore.Interfaces;
using System.Threading;
using DefCore.Structures;
using FarseerPhysics.Dynamics;
using FarseerPhysics.DebugViews;
using FarseerPhysics;
using Ivory.Interface;
using Ivory.Libs;
using DefCore.Actors; 

namespace DefCore
{
    public abstract class LevelBase : GameScreen
    {
        //Fields
        public int WorldWidth { get { return GameCellMap.MapWidth; } }
        public int WorldPixelWidth { get { return (int)(WorldWidth * Globals.TileSize.X); } }

        public int WorldHeight { get { return GameCellMap.MapHeight; } }
        public int WorldPixelHeight { get { return (int)(WorldHeight * Globals.TileSize.Y); } }

        public EnumLevelToolsMode Tool { get; set; }

        //Game Objects    
        public CellMap GameCellMap { get; set; }
        public Director DirectionalUnit { get; set; }
        public UserInterface Interface { get; set; }
        public StructuralControlUnit StructureControlUnit { get; private set; }

        //Construct
        public LevelBase()
            : base(Objects.GameObject)
        {

        }
        
        //Methods 
        protected abstract void LevelRender(SpriteBatch batch, GameTime time);

        protected abstract void OnSolve(InputControl input, GameTime time);
        protected abstract bool OnLoadingScreen();
        protected abstract void LoadLevelFile();
        protected abstract void LoadInterface(UserInterface gui);

        public override void RenderGame(SpriteBatch batch, GameTime time)
        {
            GameCellMap.CreateBuffer(time); 

            Objects.KryptonEngine.Lights.Clear();
            Objects.KryptonEngine.Hulls.Clear();     
            Objects.KryptonEngine.Matrix = Matrix.Identity * Objects.GameCamera.KryptonView * Objects.GameCamera.KryptonProjection;

            //Build a Lighting & Hulls.            
            Objects.KryptonEngine.Lights.AddRange(GameCellMap.LightsInScene);
            Objects.KryptonEngine.Hulls.AddRange(GameCellMap.HullsInScene);

            Objects.KryptonEngine.Lights.AddRange(DirectionalUnit.GetLights());
            Objects.KryptonEngine.Hulls.AddRange(DirectionalUnit.GetHulls());

            Objects.KryptonEngine.Lights.AddRange(StructureControlUnit.GetLights());
            Objects.KryptonEngine.Hulls.AddRange(StructureControlUnit.GetHulls());
                       
            Game.GraphicsDevice.Clear(Color.White);
            Objects.KryptonEngine.LightMapPrepare();

            // Anything drawn here WILL be lit by Krypton.
            /* Draw the Tile Map */
            batch.Begin();
            
            //Draw the Map
            batch.Draw(GameCellMap.Buffer, new Vector2(0, 0), Color.White);

            //Draw structures
            StructureControlUnit.Draw(batch, time);
            DirectionalUnit.Render(batch, time);

            LevelRender(batch, time);
                        
            batch.End();
            Objects.KryptonEngine.Draw(time);

            //*** Anything drawn here wont be lit ***
            //  Debug Drawing
            if (Globals.EnableKryptonDebugDrawing)
                DebugDrawKrypton();
            if (Globals.EnableFarseerDebugDrawing)
                DebugDrawFarseer();

            
            batch.Begin();
            Interface.DrawGUI(batch, time);
            
            //Draw our FPS
            Process thisProc = Process.GetCurrentProcess();
            string performanceDetails = string.Format("FPS: {0}", Manager.FPS.FPS);
            performanceDetails += string.Format("\nRender Time: {0}", Manager.RenderFrameTime.ToString());
            performanceDetails += string.Format("\nLogic Time: {0}", Manager.LogicFrameTime.ToString());

            Vector2 fpsPos = new Vector2(0, Game.GraphicsDevice.Viewport.Height - FontBin.Instance.GetFnt("Arial8").MeasureString(performanceDetails).Y);

            batch.DrawString(FontBin.Instance.GetFnt("Arial8"), performanceDetails, fpsPos + Vector2.One, Color.Black);
            batch.DrawString(FontBin.Instance.GetFnt("Arial8"), performanceDetails, fpsPos, Color.White);

            batch.End();
        }
        public override void UpdateGame(GameTime time, InputControl input)
        {
            TickControl.Instance.Update(time);
            Objects.GameWorld.Step(Math.Min((float)time.ElapsedGameTime.TotalSeconds, (1f / 30f)));            

            if (input.IsNewKeyPress(Keys.F3))
                Manager.SwitchToMainMenu();
            if (input.IsNewKeyPress(Keys.F6))
                Globals.EnableKryptonDebugDrawing = !Globals.EnableKryptonDebugDrawing;
            if (input.IsNewKeyPress(Keys.F7))
                Globals.EnableFarseerDebugDrawing = !Globals.EnableFarseerDebugDrawing;


            //Handle camera Movement
            SelectionEngine.Instance.Update(this, input, time);
            Objects.GameCamera.Solve(input, time);

            GameCellMap.SolveCellMap(input, time);
            DirectionalUnit.Solve(input, time);

            CellMap.RenderStructureFrames = Tool == EnumLevelToolsMode.BUILDINGTOOL;
            StructureControlUnit.Solve(input, time);
                                   
            this.OnSolve(input, time);
        }

        public override void RenderUI(SpriteBatch batch, GameTime time) { }
        public override void UpdateUI(GameTime time, InputControl input) { }

        public override bool PreloadAssets()
        {
            this.LoadingScreen = new LoadScreen(Game, FontBin.Instance.GetLoc("Arial12"), Color.Black, Color.White);
            return true;
        }
        public override bool LoadData()
        {
            Objects.GameWorld = new World(Vector2.Zero);
            Objects.FarseerDebugView = new DebugViewXNA(Objects.GameWorld);
            Objects.FarseerDebugView.Flags = 0 | DebugViewFlags.Shape;
            Objects.FarseerDebugView.LoadContent(Game.GraphicsDevice, Game.Content);

            Objects.KryptonEngine = new Krypton.KryptonEngine(Game, "src/Lighting32T0091");
            Objects.KryptonEngine.Initialize();
            Objects.KryptonEngine.CullMode = CullMode.None;
            Objects.KryptonEngine.Bluriness = 1;

            byte ambience = (byte)(Byte.MaxValue * Globals.AmbientLevel);
            Objects.KryptonEngine.AmbientColor = new Color(ambience, ambience, ambience);
            Objects.GameCamera = new Camera(this);

            this.Interface = new UserInterface();
            this.LoadLevelFile();
            this.GameCellMap.LoadComponents();
            this.StructureControlUnit = new StructuralControlUnit(this);
            this.DirectionalUnit = new Director();
            this.OnLoadingScreen();
            
            //Create a Bound Version NUmber
            Vector2 versionPos = new Vector2(
                Game.GraphicsDevice.Viewport.Width - FontBin.Instance.GetFnt("Arial8").MeasureString(Globals.Version).X,
                Game.GraphicsDevice.Viewport.Height - FontBin.Instance.GetFnt("Arial8").MeasureString(Globals.Version).Y);
            
            Binding binding = new Binding(typeof(Globals), "Version");
            Text versionText = new Text(FontBin.Instance.GetFnt("Arial8"), binding, versionPos);
            versionText.ID = "VERSION_ALLOC";
            
            Interface.AddComponent(versionText);


            this.LoadInterface(Interface);
            
            List<ISelectable> selectables = new List<ISelectable>(GameCellMap.GetAll());
            SelectionEngine.Instance.Selectables.AddRange(selectables);
            Tool = EnumLevelToolsMode.NOTOOL;
            return true;
        }

        private void DebugDrawKrypton()
        {
            Objects.KryptonEngine.RenderHelper.Effect.CurrentTechnique = Objects.KryptonEngine.RenderHelper.Effect.Techniques["DebugDraw"];
            Game.GraphicsDevice.RasterizerState = new RasterizerState()
            {
                CullMode = CullMode.None,
                FillMode = FillMode.WireFrame,
            };

            // Clear the helpers vertices
            Objects.KryptonEngine.RenderHelper.ShadowHullVertices.Clear();
            Objects.KryptonEngine.RenderHelper.ShadowHullIndicies.Clear();

            foreach (var hull in Objects.KryptonEngine.Hulls)
                Objects.KryptonEngine.RenderHelper.BufferAddShadowHull(hull);

            foreach (Light2D light in Objects.KryptonEngine.Lights)
                Objects.KryptonEngine.RenderHelper.BufferAddBoundOutline(light.Bounds);

            foreach (var effectPass in Objects.KryptonEngine.RenderHelper.Effect.CurrentTechnique.Passes)
            {
                effectPass.Apply();
                Objects.KryptonEngine.RenderHelper.BufferDraw();
            }
        }
        private void DebugDrawFarseer()
        {
            Matrix proj = Objects.GameCamera.FarseerProjection;
            Matrix view = Objects.GameCamera.FarseerView;

            Objects.FarseerDebugView.RenderDebugData(ref proj, ref view);
        }
    }
}
